/*
 *@brief the implementation about the simple Consistent Hash Algorithm reffer to FlexiHash.
 *@author zhigang zhang 
 *@mail zhangzhigang@openv.cn 
 *@date 2010.09
 */

#include "consistent_hash.h"

#define INIT_CONSISTENT_HASH_TABLE_NODE(ht_node);                         	 \
  ht_node = pemalloc_rel(sizeof(consistent_hash_table_node), persistent); 	 \
  (*ht_node).ht = pemalloc_rel(sizeof(HashTable), persistent);               \
  zend_hash_init((*ht_node).ht, 0, NULL, ZVAL_PTR_DTOR, persistent);         \
  (*ht_node).is_sorted = 0;                                                  \

#define DESTROY_CONSISTENT_HASH_TABLE_NODE(ht_node);				\
  pefree_rel((*ht_node).ht, persistent);							\
  pefree_rel(ht_node, persistent);									\

/*new str zval is created from another*/
#define CHASH_NEW_STR_ZVAL(target_dest, target);						\
  len = Z_STRLEN_P(target); 											\
  target_dest = pemalloc_rel(sizeof(zval), persistent);					\
  Z_STRVAL_P(target_dest) = pemalloc_rel(len+1, persistent);			\
  Z_STRLEN_P(target_dest) = len;										\
  memcpy(Z_STRVAL_P(target_dest), Z_STRVAL_P(target), len+1);			\
  Z_TYPE_P(target_dest) = Z_TYPE_P(target);								\

/*{{{ consistent_hash_init_impl(zval * input, zend_bool persistent, const char * identifier, replicas)*/

consistent_hash_table_node * consistent_hash_init_impl(zval * input, zend_bool persistent, const char * identifier, uint replicas)
{
  zval** entry;
  HashPosition pos;
  char * hash_detail;
  uint hash_detail_len;
  consistent_hash_table_node* ht_node, **ht_node_tmp;

  if (Z_TYPE_P(input) != IS_ARRAY) {
    php_error_docref(NULL TSRMLS_CC, E_ERROR, "The argument should be an array!");
  }
  hash_detail_len = spprintf(&hash_detail, 0, "%s", identifier);

  //php_error_docref(NULL TSRMLS_CC, E_WARNING, "debug start ...");
  if(zend_hash_find(&EG(persistent_list), hash_detail, hash_detail_len+1, (void**)&ht_node_tmp)== FAILURE){//doesn't exist the hash table.

    NEW:INIT_CONSISTENT_HASH_TABLE_NODE(ht_node);
  
    //php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not existed!");
	
    /* Go through input array and hash the targets to dest array */
    zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos);
    while(zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &pos) == SUCCESS){

	  if(Z_TYPE_PP(entry) != IS_STRING){

		DESTROY_CONSISTENT_HASH_TABLE_NODE(ht_node);

		php_error_docref(NULL TSRMLS_CC, E_ERROR, "Each element of the hash buffer should be string!");
	  }
      consistent_hash_add_target(ht_node, *entry, replicas, persistent);
      zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos);
    }
	if(persistent){ //add the hash table node to the global persistent_list!
    	if(zend_hash_add(&EG(persistent_list), hash_detail, hash_detail_len+1, (void*)&ht_node, sizeof(consistent_hash_table_node*), NULL) == FAILURE){

			DESTROY_CONSISTENT_HASH_TABLE_NODE(ht_node);

			php_error_docref(NULL TSRMLS_CC, E_ERROR, "Register consistent_hash_table_node failure!");
		}
	}
  }else{ //existed
    //php_error_docref(NULL TSRMLS_CC, E_WARNING, "Existed!");
	if(persistent){
    	ht_node = *ht_node_tmp;
	}else{
		zend_hash_del(&EG(persistent_list), hash_detail, hash_detail_len+1); //del the hash node!
		DESTROY_CONSISTENT_HASH_TABLE_NODE(ht_node);
		goto NEW;
	}
  }

  if(consistent_hash_sort_targets_by_position(ht_node) == FAILURE){
	  php_error_docref(NULL TSRMLS_CC, E_ERROR, "The consistent_hash_table_node is sorted failure!");
  }

  return ht_node;
}
/*}}}*/

/*
 *@brief lookup.
 */
/*{{{ consistent_hash_lookup_impl(consistent_hash_table_node* ht_node, const char* key)*/
zval * consistent_hash_lookup_impl(consistent_hash_table_node* ht_node, const char* key)
{
  HashPosition pos;
  zval ** entry;
  char * string_key;
  uint k_hash, string_key_len;

  k_hash = crc32(key); 

  zend_hash_internal_pointer_reset_ex(ht_node->ht, &pos);
  while(zend_hash_get_current_data_ex(ht_node->ht, (void **)&entry, &pos) == SUCCESS){
    zend_hash_get_current_key_ex(ht_node->ht, &string_key, &string_key_len, NULL, 0, &pos);
    if(k_hash <= atoi(string_key)){
      return *entry;
    }
    zend_hash_move_forward_ex(ht_node->ht, &pos);
  }
//from start the hash table return 1 element.
  zend_hash_internal_pointer_reset_ex(ht_node->ht, &pos);
  while(zend_hash_get_current_data_ex(ht_node->ht, (void**)&entry, &pos)){
      return *entry;
  }

}

/*}}}*/

/*
 *@brief add the target to HashTable.
 */
/*{{{ consistent_hash_add_target(consistent_hash_table_node* ht_node, zval* target, uint replicas, zend_bool persistent)*/
static zend_bool consistent_hash_add_target(consistent_hash_table_node* ht_node, zval* target, uint replicas, zend_bool persistent)
{
  uint i, len, n_num, hash_len, hash;
  char * target_str, *chr, *hash_str;
  zval * target_dest;

  /*create the new zval for consistent_hash_table_node*/
  CHASH_NEW_STR_ZVAL(target_dest, target);

  for(i=0;i<replicas;i++){
    n_num = spprintf(&chr, 0, "%d", i); //itoa
    len += n_num;
    target_str = emalloc_rel(len+1);
    //memset(target_str, 0, len);
    memcpy(target_str, Z_STRVAL_P(target)/*(*target).value.str.val*/, len+1);
    target_str  = strcat(target_str, chr);
	//php_error_docref(NULL TSRMLS_CC, E_WARNING, target_str);
    hash = crc32(target_str);
    efree_rel(target_str);

    hash_len = spprintf(&hash_str, 0, "%u", hash);

	(*target_dest).refcount++;
    if(zend_hash_update(ht_node->ht, hash_str, hash_len+1, (void *)&target_dest, sizeof(zval*), NULL) == FAILURE){
      return FAILURE;
    }
  }

  return SUCCESS;
}
/*}}}*/

/*
 *@brief sort the hash table by position.
 */
/*{{{ consistent_hash_sort_targets_by_position(consistent_hash_table_node * ht_node)*/
static zend_bool consistent_hash_sort_targets_by_position(consistent_hash_table_node * ht_node)
{
  if(ht_node->is_sorted){
    return SUCCESS;
  }
  if(ksort(ht_node->ht) == FAILURE){
	return FAILURE;
  }
  ht_node->is_sorted = 1;
  return SUCCESS;
}
/*}}}*/

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim600: noet sw=4 ts=4 fdm=marker
 * vim<600: noet sw=4 ts=4
 */

